{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "fbb1824a-cd01-4258-8add-d0773ae69fcc",
   "metadata": {},
   "source": [
    "## Neo4j Langchain\n",
    "\n",
    "https://python.langchain.com/docs/use_cases/graph/\n",
    "\n",
    "https://python.langchain.com/docs/integrations/graphs/neo4j_cypher/\n",
    "\n",
    "https://python.langchain.com/docs/use_cases/graph/prompting/#setup\n",
    "\n",
    "https://python.langchain.com/docs/integrations/vectorstores/neo4jvector/\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "b0532eb7",
   "metadata": {},
   "outputs": [],
   "source": [
    "# !pip3 install --upgrade --quiet  langchain langchain-community langchain-openai neo4j"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "8667949b",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Name: langchain\n",
      "Version: 0.1.16\n",
      "Summary: Building applications with LLMs through composability\n",
      "Home-page: https://github.com/langchain-ai/langchain\n",
      "Author: \n",
      "Author-email: \n",
      "License: MIT\n",
      "Location: /Library/Frameworks/Python.framework/Versions/3.12/lib/python3.12/site-packages\n",
      "Requires: aiohttp, dataclasses-json, jsonpatch, langchain-community, langchain-core, langchain-text-splitters, langsmith, numpy, pydantic, PyYAML, requests, SQLAlchemy, tenacity\n",
      "Required-by: \n",
      "---\n",
      "Name: neo4j\n",
      "Version: 5.19.0\n",
      "Summary: Neo4j Bolt driver for Python\n",
      "Home-page: \n",
      "Author: \n",
      "Author-email: \"Neo4j, Inc.\" <drivers@neo4j.com>\n",
      "License: Apache License, Version 2.0\n",
      "Location: /Library/Frameworks/Python.framework/Versions/3.12/lib/python3.12/site-packages\n",
      "Requires: pytz\n",
      "Required-by: \n"
     ]
    }
   ],
   "source": [
    "!pip show langchain neo4j"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "b87033b3",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[{'n': {'name': 'Personal Banking Customer',\n",
       "   'description': 'A customer of the bank, with personal bank accounts.',\n",
       "   'tags': 'Element,Person,Customer'}},\n",
       " {'n': {'name': 'Customer Service Staff',\n",
       "   'description': 'Customer service staff within the bank.',\n",
       "   'tags': 'Element,Person,Bank Staff'}},\n",
       " {'n': {'name': 'Back Office Staff',\n",
       "   'description': 'Administration and support staff within the bank.',\n",
       "   'tags': 'Element,Person,Bank Staff'}}]"
      ]
     },
     "execution_count": 3,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from langchain_community.graphs import Neo4jGraph\n",
    "# graph = Neo4jGraph(url=\"bolt://localhost:7687\", username=\"neo4j\", password=\"neo4j123\")\n",
    "\n",
    "import os\n",
    "os.environ[\"NEO4J_URI\"] = \"bolt://localhost:7687\"\n",
    "os.environ[\"NEO4J_USERNAME\"] = \"neo4j\"\n",
    "os.environ[\"NEO4J_PASSWORD\"] = \"neo4j123\"\n",
    "\n",
    "graph = Neo4jGraph()\n",
    "\n",
    "graph.query(\"MATCH (n:Person) RETURN n\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "id": "dd6c9c3b",
   "metadata": {},
   "outputs": [],
   "source": [
    "from langchain.chains import GraphCypherQAChain\n",
    "# from langchain_openai import ChatOpenAI\n",
    "# llm = ChatOpenAI(model=\"gpt-3.5-turbo\", temperature=0)\n",
    "\n",
    "import utility\n",
    "from langchain_mistralai.chat_models import ChatMistralAI\n",
    "\n",
    "llm = ChatMistralAI(mistral_api_key=utility.get_env(\"MISTRAL_API_KEY\",None), model=\"open-mistral-7b\", temperature=0)\n",
    "\n",
    "chain = GraphCypherQAChain.from_llm(graph=graph, llm=llm, verbose=True)\n",
    "# top_k=2\n",
    "# return_intermediate_steps=True\n",
    "# return_direct=True\n",
    "\n",
    "# from langchain_core.prompts import ChatPromptTemplate\n",
    "# from langchain_core.runnables import RunnableParallel, RunnablePassthrough\n",
    "# prompt = ChatPromptTemplate.from_template(\"What software systems are used by Back Office Staff?\")\n",
    "\n",
    "# from langchain_core.output_parsers import StrOutputParser\n",
    "\n",
    "# output_parser = StrOutputParser()\n",
    "\n",
    "\n",
    "# chain = llm | output_parser"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "id": "d9b3782f",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "\n",
      "\u001b[1m> Entering new GraphCypherQAChain chain...\u001b[0m\n",
      "Generated Cypher:\n",
      "\u001b[32;1m\u001b[1;3mcypher\n",
      "MATCH (p:Person {name: \"Back Office Staff\"})-[:Uses]->(ss:SoftwareSystem)\n",
      "RETURN ss.name\n",
      "\u001b[0m\n",
      "Full Context:\n",
      "\u001b[32;1m\u001b[1;3m[{'ss.name': 'Mainframe Banking System'}]\u001b[0m\n",
      "\n",
      "\u001b[1m> Finished chain.\u001b[0m\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "{'query': 'What software systems are used by Back Office Staff?',\n",
       " 'result': 'The Back Office Staff uses the Mainframe Banking System.'}"
      ]
     },
     "execution_count": 13,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "response = chain.invoke({\"query\": \"What software systems are used by Back Office Staff?\"})\n",
    "response"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "id": "f46f7b1f",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "\n",
      "\u001b[1m> Entering new GraphCypherQAChain chain...\u001b[0m\n",
      "Generated Cypher:\n",
      "\u001b[32;1m\u001b[1;3mcypher\n",
      "MATCH (p:Person {name: \"Personal Banking Customer\"})-[:Uses]->(ss:SoftwareSystem)\n",
      "RETURN ss.name\n",
      "\u001b[0m\n",
      "Full Context:\n",
      "\u001b[32;1m\u001b[1;3m[{'ss.name': 'ATM'}, {'ss.name': 'Internet Banking System'}]\u001b[0m\n",
      "\n",
      "\u001b[1m> Finished chain.\u001b[0m\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "{'query': 'What software systems are used by Personal Banking Customer?',\n",
       " 'result': 'Personal Banking Customers use ATM and Internet Banking System software systems.'}"
      ]
     },
     "execution_count": 14,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "response = chain.invoke({\"query\": \"What software systems are used by Personal Banking Customer?\"})\n",
    "response"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "id": "07b0ce89",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "\n",
      "\u001b[1m> Entering new GraphCypherQAChain chain...\u001b[0m\n",
      "Generated Cypher:\n",
      "\u001b[32;1m\u001b[1;3mcypher\n",
      "MATCH (p:Person)-[:Uses]->(ss:SoftwareSystem)\n",
      "WHERE p.name =~ 'Bank Staff'\n",
      "RETURN ss.name\n",
      "\u001b[0m\n",
      "Full Context:\n",
      "\u001b[32;1m\u001b[1;3m[]\u001b[0m\n",
      "\n",
      "\u001b[1m> Finished chain.\u001b[0m\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "{'query': 'What software systems are used by Bank Staff?',\n",
       " 'result': \"I'm unable to provide an answer as the given information does not specify the software systems used by Bank Staff.\"}"
      ]
     },
     "execution_count": 15,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "response = chain.invoke({\"query\": \"What software systems are used by Bank Staff?\"})\n",
    "response"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 44,
   "id": "11c55796",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "\n",
      "\u001b[1m> Entering new GraphCypherQAChain chain...\u001b[0m\n",
      "Generated Cypher:\n",
      "\u001b[32;1m\u001b[1;3mcypher\n",
      "MATCH (p:Person)-[:Uses]->(ss:SoftwareSystem)\n",
      "WHERE p.tags CONTAINS 'Bank Staff'\n",
      "RETURN ss.name\n",
      "\u001b[0m\n",
      "Full Context:\n",
      "\u001b[32;1m\u001b[1;3m[{'ss.name': 'Mainframe Banking System'}, {'ss.name': 'Mainframe Banking System'}]\u001b[0m\n",
      "\n",
      "\u001b[1m> Finished chain.\u001b[0m\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "{'query': \"What software systems are used by Bank Staff? Please check whether the 'tags' properties in Person contains the person \",\n",
       " 'result': \"The information provided indicates that 'Mainframe Banking System' is mentioned twice. Therefore, the answer would be:\\n\\nHelpful Answer: Bank staff use Mainframe Banking Systems.\"}"
      ]
     },
     "execution_count": 44,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "response = chain.invoke({\"query\": \"What software systems are used by Bank Staff? Please check whether the 'tags' properties in Person contains the person \"})\n",
    "response"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 57,
   "id": "ca561b50",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "\n",
      "\u001b[1m> Entering new GraphCypherQAChain chain...\u001b[0m\n",
      "Generated Cypher:\n",
      "\u001b[32;1m\u001b[1;3mcypher\n",
      "MATCH (p:Person)-[:Uses]->(ss:SoftwareSystem)\n",
      "WHERE p.tags CONTAINS 'Customer' AND ss.tags IS NOT NULL\n",
      "RETURN ss.name, ss.description\n",
      "\u001b[0m\n",
      "Full Context:\n",
      "\u001b[32;1m\u001b[1;3m[{'ss.name': 'ATM', 'ss.description': 'Allows customers to withdraw cash.'}, {'ss.name': 'Internet Banking System', 'ss.description': 'Allows customers to view information about their bank accounts, and make payments.'}]\u001b[0m\n",
      "\n",
      "\u001b[1m> Finished chain.\u001b[0m\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "{'query': \"What software systems are used by customers? Use WHERE clause to check whether the value of 'tags' property contains 'Customer\",\n",
       " 'result': \"The ATM and Internet Banking System are the software systems used by customers. To check if a specific system is used by customers with the 'Customer' tag, you can use a WHERE clause with the 'tags' property. For example:\\n\\n```cypher\\nMATCH (s:SoftwareSystem {name: 'ATM'})-[:USED_BY]->(c:Customer)\\nWHERE c.tags CONTAINS 'Customer'\\nRETURN s.name AS software_system\\n\\nMATCH (s:SoftwareSystem {name: 'Internet Banking System'})-[:USED_BY]->(c:Customer)\\nWHERE c.tags CONTAINS 'Customer'\\nRETURN s.name AS software_system\\n```\\n\\nThis query will return the names of the software systems that are used by customers with the 'Customer' tag.\"}"
      ]
     },
     "execution_count": 57,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "response = chain.invoke({\"query\": \"What software systems are used by customers? Use WHERE clause to check whether the value of 'tags' property contains 'Customer\"})\n",
    "response"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "214f2f09",
   "metadata": {},
   "source": [
    "## Few-shot examples\n",
    "    "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "abb2c24f",
   "metadata": {},
   "outputs": [],
   "source": [
    "examples = [\n",
    "    {\n",
    "        \"question\": \"List all the users\",\n",
    "        \"query\": \"MATCH (p:Person) RETURN distinct p\",\n",
    "    },\n",
    "    {\n",
    "        \"question\": \"what software systems are used by customer\",\n",
    "        \"query\": \"MATCH (p:Person)-[r]->(s:SoftwareSystem) WHERE p.tags contains 'Customer' RETURN DISTINCT s.name,s.description\",\n",
    "    },\n",
    "    {\n",
    "        \"question\": \"what software systems are used by staff\",\n",
    "        \"query\": \"MATCH (p:Person)-[r]->(s:SoftwareSystem) WHERE p.tags contains 'Bank Staff' RETURN DISTINCT s.name,s.description\",\n",
    "    }\n",
    "]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "abeedf5c-8735-4419-9a08-324dd5554dc8",
   "metadata": {},
   "outputs": [],
   "source": [
    "from langchain_core.prompts import FewShotPromptTemplate, PromptTemplate\n",
    "\n",
    "example_prompt = PromptTemplate.from_template(\n",
    "    \"Question: {question}\\nCypher query: {query}\"\n",
    ")\n",
    "# prompt = FewShotPromptTemplate(\n",
    "#     examples=examples,\n",
    "#     example_prompt=example_prompt,\n",
    "#     prefix=\"You are a Neo4j expert. Given an input question, create a syntactically correct Cypher query to run.\\n\\nHere is the schema information\\n{schema}.\\n\\nBelow are a number of examples of questions and their corresponding Cypher queries.\",\n",
    "#     suffix=\"Question: {question}\\nCypher query: \",\n",
    "#     input_variables=[\"question\", \"schema\"],\n",
    "# )\n",
    "prompt = FewShotPromptTemplate(\n",
    "    examples=examples,\n",
    "    example_prompt=example_prompt,\n",
    "    prefix=\"You are a Neo4j expert. Given an input question, create a syntactically correct Cypher query to run.\\n\\nBelow are a number of examples of questions and their corresponding Cypher queries.\",\n",
    "    suffix=\"Question: {question}\\nCypher query: \",\n",
    "    input_variables=[\"question\"],\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "2ae5b263",
   "metadata": {},
   "outputs": [],
   "source": [
    "# print(prompt.format(question=\"What software systems are used by Staff?\", schema=\"neo4j\"))\n",
    "print(prompt.format(question=\"What software systems are used by Staff?\"))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "f9926c14",
   "metadata": {},
   "outputs": [],
   "source": [
    "chain = GraphCypherQAChain.from_llm(graph=graph, llm=llm, cypher_prompt=prompt, verbose=True)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "d4a2d0bd",
   "metadata": {},
   "outputs": [],
   "source": [
    "\n",
    "response = chain.invoke({\"query\": \"What software systems are used by customer?\"})\n",
    "response\n"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.12.1"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
